home *** CD-ROM | disk | FTP | other *** search
- /*
- * Program: Interactive Mail Access Protocol 2 (IMAP2) routines
- *
- * Author: Mark Crispin
- * Networks and Distributed Computing
- * Computing & Communications
- * University of Washington
- * Administration Building, AG-44
- * Seattle, WA 98195
- * Internet: MRC@CAC.Washington.EDU
- *
- * Date: 15 June 1988
- * Last Edited: 11 May 1992
- *
- * Sponsorship: The original version of this work was developed in the
- * Symbolic Systems Resources Group of the Knowledge Systems
- * Laboratory at Stanford University in 1987-88, and was funded
- * by the Biomedical Research Technology Program of the National
- * Institutes of Health under grant number RR-00785.
- *
- * Original version Copyright 1988 by The Leland Stanford Junior University.
- * Copyright 1992 by the University of Washington.
- *
- * Permission to use, copy, modify, and distribute this software and its
- * documentation for any purpose and without fee is hereby granted, provided
- * that the above copyright notices appear in all copies and that both the
- * above copyright notices and this permission notice appear in supporting
- * documentation, and that the name of the University of Washington or The
- * Leland Stanford Junior University not be used in advertising or publicity
- * pertaining to distribution of the software without specific, written prior
- * permission. This software is made available "as is", and
- * THE UNIVERSITY OF WASHINGTON AND THE LELAND STANFORD JUNIOR UNIVERSITY
- * DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED, WITH REGARD TO THIS SOFTWARE,
- * INCLUDING WITHOUT LIMITATION ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
- * FITNESS FOR A PARTICULAR PURPOSE, AND IN NO EVENT SHALL THE UNIVERSITY OF
- * WASHINGTON OR THE LELAND STANFORD JUNIOR UNIVERSITY BE LIABLE FOR ANY
- * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
- * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
- * CONTRACT, TORT (INCLUDING NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- *
- */
-
- #include <ctype.h>
- #include <stdio.h>
- #if unix
- #include <sys/types.h>
- #include <pwd.h>
- #include <sys/file.h>
- #include <sys/stat.h>
- #endif
- #include "osdep.h"
- #include "mail.h"
- #include "imap2.h"
- #include "misc.h"
-
-
- /* Driver dispatch used by MAIL */
-
- DRIVER imapdriver = {
- (DRIVER *) NIL, /* next driver */
- map_valid, /* mailbox is valid for us */
- map_find, /* find mailboxes */
- map_find_bboards, /* find bboards */
- map_open, /* open mailbox */
- map_close, /* close mailbox */
- map_fetchfast, /* fetch message "fast" attributes */
- map_fetchflags, /* fetch message flags */
- map_fetchenvelope, /* fetch message envelopes */
- map_fetchheader, /* fetch message header only */
- map_fetchtext, /* fetch message body only */
- map_fetchbody, /* fetch message body section */
- map_setflag, /* set message flag */
- map_clearflag, /* clear message flag */
- map_search, /* search for message based on criteria */
- map_ping, /* ping mailbox to see if still alive */
- map_check, /* check for new messages */
- map_expunge, /* expunge deleted messages */
- map_copy, /* copy messages to another mailbox */
- map_move, /* move messages to another mailbox */
- map_gc /* garbage collect stream */
- };
-
-
- /* Mail Access Protocol validate mailbox
- * Accepts: mailbox name
- * Returns: our driver if name is valid, otherwise calls valid in next driver
- */
-
- DRIVER *map_valid (name)
- char *name;
- { /* assume IMAP if starts with host delimiter */
- if ((*name == '{') || (*name == '*' && name[1] == '{')) return &imapdriver;
- /* else let next driver have a try */
- else return imapdriver.next ? (*imapdriver.next->valid) (name) : NIL;
- }
-
- /* Mail Access Protocol find list of mailboxes
- * Accepts: mail stream
- * pattern to search
- */
-
- void map_find (stream,pat)
- MAILSTREAM *stream;
- char *pat;
- {
- if (stream) { /* have a mailbox stream open? */
- if (LOCAL && LOCAL->use_find &&
- !strcmp (imap_send (stream,"FIND MAILBOXES",pat,NIL)->key,"BAD"))
- LOCAL->use_find = NIL; /* note no finding with this server */
- }
- #if unix /* barf */
- else { /* make file name */
- long fd;
- char tmp[MAILTMPLEN];
- char *s,*t;
- struct stat sbuf;
- sprintf (tmp,"%s/.mailboxlist",getpwuid (geteuid ())->pw_dir);
- if ((fd = open (tmp,O_RDONLY,NIL)) >= 0) {
- fstat (fd,&sbuf); /* get file size and read data */
- read (fd,s = (char *) fs_get (sbuf.st_size + 1),sbuf.st_size);
- close (fd); /* close file */
- s[sbuf.st_size] = '\0'; /* tie off string */
- if (t = strtok (s,"\n")) /* get first mailbox name */
- do if (*t == '{' && pmatch (t,pat)) mm_mailbox (t);
- while (t = strtok (NIL,"\n"));
- }
- }
- #endif
- }
-
-
- /* Mail Access Protocol find list of bboards
- * Accepts: mail stream
- * pattern to search
- */
-
- void map_find_bboards (stream,pat)
- MAILSTREAM *stream;
- char *pat;
- {
- /* this is optional, so no complaint if fail */
- if (stream && LOCAL && LOCAL->use_find && LOCAL->use_bboard &&
- !strcmp (imap_send (stream,"FIND BBOARDS",pat,NIL)->key,"BAD"))
- LOCAL->use_bboard = NIL;
- }
-
- /* Mail Access Protocol open
- * Accepts: stream to open
- * Returns: stream to use on success, NIL on failure
- */
-
- MAILSTREAM *map_open (stream)
- MAILSTREAM *stream;
- {
- long i,j;
- char username[MAILTMPLEN];
- char pwd[MAILTMPLEN];
- char tmp[MAILTMPLEN];
- char *host;
- char *mailbox;
- IMAPPARSEDREPLY *reply;
- long bboard = *stream->mailbox == '*';
- char *base = bboard ? stream->mailbox + 1 : stream->mailbox;
- if (!(host = imap_hostfield (base))) mm_log ("Host not specified",ERROR);
- else {
- if (!(mailbox = imap_mailboxfield (base))) {
- fs_give ((void **) &host);/* prevent storage leaks */
- mm_log ("Mailbox not specified",ERROR);
- }
- else {
- if (LOCAL) { /* if stream opened earlier by us */
- if (strcmp (ucase (strcpy (tmp,host)),
- ucase (strcpy (pwd,imap_host (stream))))) {
- /* if hosts are different punt it */
- sprintf (tmp,"Closing connection to %s",imap_host (stream));
- if (!stream->silent) mm_log (tmp,(long) NIL);
- map_close (stream);
- }
- else { /* else recycle if still alive */
- i = stream->silent; /* temporarily mark silent */
- stream->silent = T; /* don't give mm_exists() events */
- j = map_ping (stream);/* learn if stream still alive */
- stream->silent = i; /* restore prior state */
- if (j) { /* was stream still alive? */
- sprintf (tmp,"Reusing connection to %s",host);
- if (!stream->silent) mm_log (tmp,(long) NIL);
- for (i = 0; i < LOCAL->cachesize; ++i) {
- if (LOCAL->header[i]) fs_give ((void **) &LOCAL->header[i]);
- if (LOCAL->text[i]) fs_give ((void **) &LOCAL->text[i]);
- }
- fs_give ((void **) &LOCAL->header);
- fs_give ((void **) &LOCAL->text);
- LOCAL->cachesize = 0;/* cache has been zapped */
- }
- else map_close (stream);
- }
- }
-
- if (!LOCAL) { /* open new connection if no recycle */
- stream->local = fs_get (sizeof (IMAPLOCAL));
- LOCAL->reply.line = LOCAL->reply.tag = LOCAL->reply.key =
- LOCAL->reply.text = NIL;
- /* assume maximal server */
- LOCAL->use_body = LOCAL->use_find = LOCAL->use_bboard = T;
- LOCAL->cachesize = 0; /* initially no caches */
- LOCAL->header = LOCAL->text = NIL;
- /* try authenticated open */
- if (LOCAL->tcpstream = tcp_aopen (host,"/etc/rimapd")) {
- /* if success, see if reasonable banner */
- if ((reply = imap_parse_reply (stream,tcp_getline(LOCAL->tcpstream)))
- && !strcmp (reply->tag,"*") && !strcmp (reply->key,"PREAUTH"))
- mm_notify (stream,reply->text,(long) NIL);
- /* nuke the stream then */
- else if (LOCAL->tcpstream) {
- tcp_close (LOCAL->tcpstream);
- LOCAL->tcpstream = NIL;
- }
- }
- if (!LOCAL->tcpstream &&
- (LOCAL->tcpstream = tcp_open (host,IMAPTCPPORT))) {
- /* get greeting */
- if (!imap_OK (stream,reply = imap_reply (stream,NIL))) {
- mm_log (reply->text,ERROR);
- map_close (stream); /* clean up */
- }
- else { /* only so many tries to login */
- for (i = 0; i < MAXLOGINTRIALS; ++i) {
- *pwd = 0; /* initialize the password */
- /* get username and password from user */
- mm_login (tcp_host (LOCAL->tcpstream),username,pwd,i);
- /* abort if he refuses to give a password */
- if (*pwd == '\0') i = MAXLOGINTRIALS;
- else { /* send "LOGIN username pwd" */
- j =strlen (pwd);/* portability madness */
- sprintf (tmp,"{%ld}\015\012%s",j,pwd);
- if (imap_OK (stream,reply = imap_send (stream,"LOGIN",username,
- tmp))) break;
- /* output failure and try again */
- sprintf (tmp,"Login failed: %.80s",reply->text);
- mm_log (tmp,WARN);
- /* give up now if connection died */
- if (!strcmp (reply->key,"BYE")) i = MAXLOGINTRIALS;
- }
- }
- /* give up if too many failures */
- if (i >= MAXLOGINTRIALS) {
- mm_log (*pwd ? "Too many login failures":"Login aborted",ERROR);
- map_close (stream);
- }
- }
- }
- /* failed utterly to open */
- if (LOCAL && !LOCAL->tcpstream) map_close (stream);
- }
-
- if (LOCAL) { /* have a connection now??? */
- stream->sequence++; /* bump sequence number */
- /* prepare to update mailbox name */
- fs_give ((void **) &stream->mailbox);
- /* send "SELECT mailbox" */
- j = strlen (mailbox); /* portability madness */
- sprintf (tmp,"{%ld}\015\012%s",j,mailbox);
- if (!imap_OK (stream,reply = imap_send (stream,bboard ? "BBOARD" :
- "SELECT",tmp,NIL))) {
- sprintf (tmp,"{%s}<no mailbox>",imap_host (stream));
- stream->mailbox = cpystr (tmp);
- sprintf (tmp,"Can't select mailbox: %.80s",reply->text);
- mm_log (tmp,ERROR);
- /* make sure dummy message counts */
- mail_exists (stream,(long) 0);
- mail_recent (stream,(long) 0);
- }
- else { /* update mailbox name */
- sprintf (tmp,"%s{%s}%s",bboard ? "*" : "",imap_host(stream),mailbox);
- stream->mailbox = cpystr (tmp);
- /* note if server said it was readonly */
- reply->text[11] = '\0';
- stream->readonly = !strcmp (ucase (reply->text),"[READ-ONLY]");
- }
- if (!(stream->nmsgs || stream->silent))
- mm_log ("Mailbox is empty",(long) NIL);
- }
- else { /* failed to open, prevent storage leaks */
- fs_give ((void **) &host);
- fs_give ((void **) &mailbox);
- }
- }
- }
- /* give up if nuked during startup */
- if (LOCAL && !LOCAL->tcpstream) map_close (stream);
- return LOCAL ? stream : NIL; /* if stream is alive, return to caller */
- }
-
- /* Mail Access Protocol close
- * Accepts: MAIL stream
- */
-
- void map_close (stream)
- MAILSTREAM *stream;
- {
- long i;
- IMAPPARSEDREPLY *reply;
- if (stream && LOCAL) { /* send "LOGOUT" */
- if (LOCAL->tcpstream &&
- !imap_OK (stream,reply = imap_send (stream,"LOGOUT",NIL,NIL))) {
- sprintf (LOCAL->tmp,"Error in logout: %.80s",reply->text);
- mm_log (LOCAL->tmp,WARN);
- }
- /* close TCP connection if still open */
- if (LOCAL->tcpstream) tcp_close (LOCAL->tcpstream);
- /* free up memory */
- if (LOCAL->reply.line) fs_give ((void **) &LOCAL->reply.line);
- /* nuke the cache */
- for (i = 0; i < LOCAL->cachesize; ++i) {
- if (LOCAL->header[i]) fs_give ((void **) &LOCAL->header[i]);
- if (LOCAL->text[i]) fs_give ((void **) &LOCAL->text[i]);
- }
- fs_give ((void **) &LOCAL->header);
- fs_give ((void **) &LOCAL->text);
- /* nuke the local data */
- fs_give ((void **) &stream->local);
- }
- }
-
-
- /* Mail Access Protocol fetch fast information
- * Accepts: MAIL stream
- * sequence
- *
- * Generally, map_fetchenvelope is preferred
- */
-
- void map_fetchfast (stream,sequence)
- MAILSTREAM *stream;
- char *sequence;
- { /* send "FETCH sequence FAST" */
- IMAPPARSEDREPLY *reply;
- if (!imap_OK (stream,reply = imap_send (stream,"FETCH",sequence,"FAST"))) {
- sprintf (LOCAL->tmp,"Fetch fast rejected: %.80s",reply->text);
- mm_log (LOCAL->tmp,ERROR);
- }
- }
-
- /* Mail Access Protocol fetch flags
- * Accepts: MAIL stream
- * sequence
- */
-
- void map_fetchflags (stream,sequence)
- MAILSTREAM *stream;
- char *sequence;
- { /* send "FETCH sequence FLAGS" */
- IMAPPARSEDREPLY *reply;
- if (!imap_OK (stream,reply = imap_send (stream,"FETCH",sequence,"FLAGS"))){
- sprintf (LOCAL->tmp,"Fetch flags rejected: %.80s",reply->text);
- mm_log (LOCAL->tmp,ERROR);
- }
- }
-
-
- /* Mail Access Protocol fetch envelope
- * Accepts: MAIL stream
- * message # to fetch
- * Returns: envelope of this message
- *
- * Fetches the "fast" information as well
- */
-
- ENVELOPE *map_fetchenvelope (stream,msgno)
- MAILSTREAM *stream;
- long msgno;
- {
- long i = msgno;
- long j = min (msgno+MAPLOOKAHEAD-1,stream->nmsgs);
- char seq[20];
- IMAPPARSEDREPLY *reply;
- MESSAGECACHE *elt = mail_elt (stream,msgno);
- if (!elt->env) { /* if we don't have an envelope */
- if (msgno != stream->nmsgs) /* never lookahead if last message */
- while (i < j && !(mail_elt (stream,i+1)->env)) i++;
- sprintf (seq,"%ld:%ld",msgno,i);
- if (LOCAL->use_body) { /* build "FETCH msgno:i FULL" if can do */
- if (!(strcmp ((reply = imap_send (stream,"FETCH",seq,"FULL"))->key,
- "BAD"))) LOCAL->use_body = NIL;
- }
- /* can't, send "FETCH msgno:i ALL" instead */
- if (!LOCAL->use_body) reply = imap_send (stream,"FETCH",seq,"ALL");
- if (!imap_OK (stream,reply)) {
- sprintf (LOCAL->tmp,"Fetch envelope rejected: %.80s",reply->text);
- mm_log (LOCAL->tmp,ERROR);
- return NIL;
- }
- }
- return elt->env; /* return the envelope */
- }
-
- /* Mail Access Protocol fetch message header
- * Accepts: MAIL stream
- * message # to fetch
- * Returns: message header in RFC822 format
- */
-
- char *map_fetchheader (stream,msgno)
- MAILSTREAM *stream;
- long msgno;
- {
- char seq[20];
- long i = msgno - 1;
- IMAPPARSEDREPLY *reply;
- if (!LOCAL->header[i]) { /* send "FETCH msgno RFC822.HEADER" */
- sprintf (seq,"%ld",msgno);
- if (!imap_OK (stream,reply = imap_send (stream,"FETCH",seq,LOCAL->text[i] ?
- "RFC822.HEADER" :
- "(RFC822.HEADER RFC822.TEXT)"))) {
- sprintf (LOCAL->tmp,"Fetch header rejected: %.80s",reply->text);
- mm_log (LOCAL->tmp,ERROR);
- }
- }
- return LOCAL->header[i] ? LOCAL->header[i] : "";
- }
-
-
- /* Mail Access Protocol fetch message text (only)
- body only;
- * Accepts: MAIL stream
- * message # to fetch
- * Returns: message text in RFC822 format
- */
-
- char *map_fetchtext (stream,msgno)
- MAILSTREAM *stream;
- long msgno;
- {
- char seq[20];
- long i = msgno - 1;
- IMAPPARSEDREPLY *reply;
- if (!LOCAL->text[i]) { /* send "FETCH msgno RFC822.TEXT" */
- sprintf (seq,"%ld",msgno);
- if (!imap_OK (stream,reply = imap_send(stream,"FETCH",seq,"RFC822.TEXT"))){
- sprintf (LOCAL->tmp,"Fetch text rejected: %.80s",reply->text);
- mm_log (LOCAL->tmp,ERROR);
- }
- }
- return LOCAL->text[i] ? LOCAL->text[i] : "";
- }
-
- /* Mail Access Protocol fetch message body as a structure
- * Accepts: Mail stream
- * message # to fetch
- * section specifier
- * pointer to length
- * Returns: pointer to section of message body
- */
-
- char *map_fetchbody (stream,m,sec,len)
- MAILSTREAM *stream;
- long m;
- char *sec;
- unsigned long *len;
- {
- BODY *b;
- PART *pt;
- char *s = sec;
- char **ss;
- unsigned long i;
- char seq[40];
- IMAPPARSEDREPLY *reply;
- *len = 0; /* in case failure */
- /* make sure have a body */
- if (!(LOCAL->use_body && map_fetchenvelope (stream,m) &&
- (b = mail_elt (stream,m)->body))) {
- /* bodies not supported, wanted section 1? */
- if (strcmp (sec,"1")) return NIL;
- /* yes, return text */
- *len = strlen (s = map_fetchtext (stream,m));
- return s;
- }
- if (!(s && *s && ((i = strtol (s,&s,10)) > 0))) return NIL;
- do { /* until find desired body part */
- /* multipart content? */
- if (b->type == TYPEMULTIPART) {
- pt = b->contents.part; /* yes, find desired part */
- while (--i && (pt = pt->next));
- if (!pt) return NIL; /* bad specifier */
- /* note new body, check valid nesting */
- if (((b = &pt->body)->type == TYPEMULTIPART) && !*s) return NIL;
- }
- else if (i != 1) return NIL;/* otherwise must be section 1 */
- /* need to go down further? */
- if (i = *s) switch (b->type) {
- case TYPEMESSAGE: /* embedded message, calculate new base */
- b = b->contents.msg.body; /* get its body, drop into multipart case */
- case TYPEMULTIPART: /* multipart, get next section */
- if ((*s++ == '.') && (i = strtol (s,&s,10)) > 0) break;
- default: /* bogus subpart specification */
- return NIL;
- }
- } while (i);
-
- /* lose if body bogus */
- if ((!b) || b->type == TYPEMULTIPART) return NIL;
- switch (b->type) { /* decide where the data is based on type */
- case TYPEMESSAGE: /* encapsulated message */
- ss = &b->contents.msg.text;
- break;
- case TYPETEXT: /* textual data */
- ss = (char **) &b->contents.text;
- break;
- default: /* otherwise assume it is binary */
- ss = (char **) &b->contents.binary;
- break;
- }
- if (!*ss) { /* fetch data if don't have it yet */
- sprintf (seq,"%ld BODY[%s]",m,sec);
- if (!imap_OK (stream,reply = imap_send (stream,"FETCH",seq,NIL))) {
- sprintf (LOCAL->tmp,"Fetch body section rejected: %.80s",reply->text);
- mm_log (LOCAL->tmp,ERROR);
- }
- }
- /* return data size if have data */
- if (s = *ss) *len = b->size.bytes;
- return s;
- }
-
- /* Mail Access Protocol set flag
- * Accepts: MAIL stream
- * sequence
- * flag(s)
- */
-
- void map_setflag (stream,sequence,flag)
- MAILSTREAM *stream;
- char *sequence;
- char *flag;
- {
- char flg[MAILTMPLEN];
- IMAPPARSEDREPLY *reply;
- /* "STORE sequence +Flags flag" */
- sprintf (flg,"+Flags %s",flag);
- if (!imap_OK (stream,reply = imap_send (stream,"STORE",sequence,flg))) {
- sprintf (LOCAL->tmp,"Set flag rejected: %.80s",reply->text);
- mm_log (LOCAL->tmp,ERROR);
- }
- }
-
-
- /* Mail Access Protocol clear flag
- * Accepts: MAIL stream
- * sequence
- * flag(s)
- */
-
- void map_clearflag (stream,sequence,flag)
- MAILSTREAM *stream;
- char *sequence;
- char *flag;
- {
- char flg[MAILTMPLEN];
- IMAPPARSEDREPLY *reply;
- /* "STORE sequence -Flags flag" */
- sprintf (flg,"-Flags %s",flag);
- if (!imap_OK (stream,reply = imap_send (stream,"STORE",sequence,flg))) {
- sprintf (LOCAL->tmp,"Clear flag rejected: %.80s",reply->text);
- mm_log (LOCAL->tmp,ERROR);
- }
- }
-
- /* Mail Access Protocol search for messages
- * Accepts: MAIL stream
- * search criteria
- */
-
- void map_search (stream,criteria)
- MAILSTREAM *stream;
- char *criteria;
- {
- long i,j;
- char *s;
- IMAPPARSEDREPLY *reply;
- MESSAGECACHE *elt;
- /* do implicit CHECK, then SEARCH */
- if (imap_OK (stream,reply = imap_send (stream,"CHECK",NIL,NIL)) &&
- imap_OK (stream,reply = imap_send (stream,"SEARCH",criteria,NIL))) {
- s = LOCAL->tmp; /* build sequence in temporary buffer */
- *s = '\0'; /* initially nothing */
- /* search through mailbox */
- for (i = 1; i <= stream->nmsgs; ++i)
- /* for searched messages with no envelope */
- if ((elt = mail_elt (stream,i)) && elt->searched && !elt->env) {
- /* prepend with comma if not first time */
- if (LOCAL->tmp[0]) *s++ = ',';
- sprintf (s,"%ld",j = i);/* output message number */
- s += strlen (s); /* point at end of string */
- /* search for possible end of range */
- while (i < stream->nmsgs && (elt = mail_elt (stream,i+1)) &&
- elt->searched && !elt->env) i++;
- if (i != j) { /* if a range */
- sprintf (s,":%ld",i); /* output delimiter and end of range */
- s += strlen (s); /* point at end of string */
- }
- }
- if (LOCAL->tmp[0]) { /* anything to pre-fetch? */
- s = cpystr (LOCAL->tmp); /* yes, copy sequence */
- if (LOCAL->use_body) { /* fetch bodies if can do */
- if (!(strcmp ("BAD", /* pre-fetch the searched messages */
- (reply = imap_send (stream,"FETCH",s,"FULL"))->key)))
- LOCAL->use_body = NIL;/* remember if we can't */
- }
- if (!LOCAL->use_body) reply = imap_send (stream,"FETCH",s,"ALL");
- if (!imap_OK (stream,reply)) {
- sprintf (LOCAL->tmp,"Search pre-fetch rejected: %.80s",reply->text);
- mm_log (LOCAL->tmp,ERROR);
- }
- fs_give ((void **) &s); /* flush copy of sequence */
- }
- }
- else {
- sprintf (LOCAL->tmp,"Search rejected: %.80s",reply->text);
- mm_log (LOCAL->tmp,ERROR);
- }
- }
-
- /* Mail Access Protocol ping mailbox
- * Accepts: MAIL stream
- * Returns: T if stream still alive, else NIL
- */
-
- long map_ping (stream)
- MAILSTREAM *stream;
- {
- return (LOCAL->tcpstream && /* send "NOOP" */
- imap_OK (stream,imap_send (stream,"NOOP",NIL,NIL))) ? T : NIL;
- }
-
-
- /* Mail Access Protocol check mailbox
- * Accepts: MAIL stream
- */
-
- void map_check (stream)
- MAILSTREAM *stream;
- {
- /* send "CHECK" */
- IMAPPARSEDREPLY *reply = imap_send (stream,"CHECK",NIL,NIL);
- if (!imap_OK (stream,reply)) {
- sprintf (LOCAL->tmp,"Check rejected: %.80s",reply->text);
- mm_log (LOCAL->tmp,ERROR);
- }
- else mm_log (reply->text,(long) NIL);
- }
-
-
- /* Mail Access Protocol expunge mailbox
- * Accepts: MAIL stream
- */
-
- void map_expunge (stream)
- MAILSTREAM *stream;
- {
- /* send "EXPUNGE" */
- IMAPPARSEDREPLY *reply = imap_send (stream,"EXPUNGE",NIL,NIL);
- if (!imap_OK (stream,reply)) {
- sprintf (LOCAL->tmp,"Expunge rejected: %.80s",reply->text);
- mm_log (LOCAL->tmp,ERROR);
- }
- else mm_log (reply->text,(long) NIL);
- }
-
- /* Mail Access Protocol copy message(s)
- s;
- * Accepts: MAIL stream
- * sequence
- * destination mailbox
- * Returns: T if successful else NIL
- */
-
- long map_copy (stream,sequence,mailbox)
- MAILSTREAM *stream;
- char *sequence;
- char *mailbox;
- {
- IMAPPARSEDREPLY *reply;
- if (!LOCAL->tcpstream) { /* not valid on dead stream */
- mm_log ("Copy rejected: connection to remote IMAP server closed",ERROR);
- return NIL;
- }
- /* send "COPY sequence mailbox" */
- if (!imap_OK (stream,reply = imap_send (stream,"COPY",sequence,mailbox))) {
- sprintf (LOCAL->tmp,"Copy rejected: %.80s",reply->text);
- mm_log (LOCAL->tmp,ERROR);
- return NIL;
- }
- map_setflag (stream,sequence,"\\Seen");
- return T;
- }
-
-
- /* Mail Access Protocol move message(s)
- s;
- * Accepts: MAIL stream
- * sequence
- * destination mailbox
- * Returns: T if successful else NIL
- */
-
- long map_move (stream,sequence,mailbox)
- MAILSTREAM *stream;
- char *sequence;
- char *mailbox;
- {
- IMAPPARSEDREPLY *reply;
- if (!LOCAL->tcpstream) { /* not valid on dead stream */
- mm_log ("Move rejected: connection to remote IMAP server closed",ERROR);
- return NIL;
- }
- /* send "COPY sequence mailbox" */
- if (!imap_OK (stream,reply = imap_send (stream,"COPY",sequence,mailbox))) {
- sprintf (LOCAL->tmp,"Move rejected: %.80s",reply->text);
- mm_log (LOCAL->tmp,ERROR);
- return NIL;
- }
- map_setflag (stream,sequence,"\\Deleted \\Seen");
- return T;
- }
-
- /* Mail Access Protocol garbage collect stream
- * Accepts: Mail stream
- * garbage collection flags
- */
-
- void map_gc (stream,gcflags)
- MAILSTREAM *stream;
- long gcflags;
- {
- unsigned long i = 0;
- MESSAGECACHE *elt;
- if (gcflags & GC_TEXTS) /* garbage collect texts? */
- while (i < stream->nmsgs) { /* flush texts from cache */
- if (LOCAL->header[i]) fs_give ((void **) &LOCAL->header[i]);
- if (LOCAL->text[i]) fs_give ((void **) &LOCAL->text[i]);
- if ((elt = stream->cache[i++])) map_gc_body (elt->body);
- }
- if (gcflags & GC_ELT) /* garbage collect cache? */
- for (i = 0; i < stream->nmsgs; i++)
- if (stream->cache[i]) mail_free_elt (&stream->cache[i]);
- }
-
-
- /* Mail Access Protocol garbage collect body texts
- * Accepts: body to GC
- */
-
- void map_gc_body (body)
- BODY *body;
- {
- PART *part;
- if (body) switch (body->type) {
- case TYPETEXT: /* unformatted text */
- if (body->contents.text) fs_give ((void **) &body->contents.text);
- break;
- case TYPEMULTIPART: /* multiple part */
- if (part = body->contents.part) do map_gc_body (&part->body);
- while (part = part->next);
- break;
- case TYPEMESSAGE: /* encapsulated message */
- map_gc_body (body->contents.msg.body);
- if (body->contents.msg.text)
- fs_give ((void **) &body->contents.msg.text);
- break;
- case TYPEAPPLICATION: /* application data */
- case TYPEAUDIO: /* audio */
- case TYPEIMAGE: /* static image */
- case TYPEVIDEO: /* video */
- if (body->contents.binary) fs_give (&body->contents.binary);
- break;
- default:
- break;
- }
- }
-
- /* Internal routines */
-
-
- /* Mail Access Protocol return host name field of "{host}mailbox"
- * Accepts: "{host}mailbox" format name
- * Returns: host name or NIL if error
- */
-
- char *imap_hostfield (string)
- char *string;
- {
- register char *start;
- char c;
- long i = 0;
- /* string must begin with "{" */
- if (*string != '{') return NIL;
- start = ++string; /* save pointer to first host char */
- /* search for closing "}" */
- while ((c = *string++) != '}') if (!c) return NIL;
- /* must have at least one char */
- if (!(i = string - start - 1)) return NIL;
- string = (char *) fs_get (i + 1);
- strncpy (string,start,i); /* copy the string */
- string[i] = '\0'; /* tie off with null */
- return string;
- }
-
-
- /* Mail Access Protocol return mailbox field of "{host}mailbox"
- * Accepts: "{host}mailbox" format name
- * Returns: mailbox name or NIL if error
- */
-
- char *imap_mailboxfield (string)
- char *string;
- {
- char c;
- /* string must begin with "{" */
- if (*string != '{') return NIL;
- /* search for closing "}" */
- while ((c = *string++) != '}') if (!c) return NIL;
- return cpystr (strlen (string) ? string : "INBOX");
- }
-
-
- /* Mail Access Protocol return host name
- * Accepts: MAIL stream
- * Returns: host name
- */
-
- char *imap_host (stream)
- MAILSTREAM *stream;
- {
- /* return host name on stream if open */
- return (LOCAL && LOCAL->tcpstream) ? tcp_host (LOCAL->tcpstream) :
- "<closed stream>";
- }
-
- /* Mail Access Protocol send command
- * Accepts: MAIL stream
- * command
- * command argument
- * Returns: parsed reply
- */
-
- IMAPPARSEDREPLY *imap_send (stream,cmd,arg,arg2)
- MAILSTREAM *stream;
- char *cmd;
- char *arg;
- char *arg2;
- {
- char tag[7];
- IMAPPARSEDREPLY *reply;
- /* gensym a new tag */
- sprintf (tag,"A%05ld",stream->gensym++);
- if (!LOCAL->tcpstream) return imap_fake (stream,tag,"OK No-op dead stream");
- mail_lock (stream); /* lock up the stream */
- if (arg2) sprintf (LOCAL->tmp,"%s %s %s %s",tag,cmd,arg,arg2);
- else if (arg) sprintf (LOCAL->tmp,"%s %s %s",tag,cmd,arg);
- else sprintf (LOCAL->tmp,"%s %s",tag,cmd);
- if (stream->debug) mm_dlog (LOCAL->tmp);
- strcat (LOCAL->tmp,"\015\012");
- /* send the command */
- if (!(tcp_soutr (LOCAL->tcpstream,LOCAL->tmp))) {
- /* close TCP connection */
- tcp_close (LOCAL->tcpstream);
- LOCAL->tcpstream = NIL;
- mail_unlock (stream); /* unlock stream */
- return imap_fake (stream,tag,"BYE IMAP connection broken in send");
- }
- /* get reply from server */
- reply = imap_reply (stream,tag);
- mail_unlock (stream); /* unlock stream */
- return reply; /* return reply to caller */
- }
-
- /* Mail Access Protocol get reply
- * Accepts: MAIL stream
- * tag to search or NIL if want a greeting
- * Returns: parsed reply, never NIL
- */
-
- IMAPPARSEDREPLY *imap_reply (stream,tag)
- MAILSTREAM *stream;
- char *tag;
- {
- IMAPPARSEDREPLY *reply;
- while (LOCAL->tcpstream) { /* parse reply from server */
- if ((reply = imap_parse_reply (stream,tcp_getline (LOCAL->tcpstream))) &&
- strcmp (reply->tag,"+")) {
- /* untagged response means unsolicited data */
- if (!strcmp (reply->tag,"*")) {
- imap_parse_unsolicited (stream,reply);
- if (tag) continue; /* waiting for a response */
- return reply; /* return greeting */
- }
- else { /* tagged reponse, return if desired reply */
- if (tag && !strcmp (tag,reply->tag)) return reply;
- /* report bogon */
- sprintf (LOCAL->tmp,"Unexpected tagged response: %.80s %.80s %.80s",
- reply->tag,reply->key,reply->text);
- mm_log (LOCAL->tmp,WARN);
- }
- }
- }
- return imap_fake (stream,tag,"BYE IMAP connection broken in reply");
- }
-
- /* Mail Access Protocol parse reply
- * Accepts: MAIL stream
- * text of reply
- * Returns: parsed reply, or NIL if can't parse at least a tag and key
- */
-
-
- IMAPPARSEDREPLY *imap_parse_reply (stream,text)
- MAILSTREAM *stream;
- char *text;
- {
- if (LOCAL->reply.line) fs_give ((void **) &LOCAL->reply.line);
- if (!(LOCAL->reply.line = text)) {
- /* NIL text means the stream died */
- tcp_close (LOCAL->tcpstream);
- LOCAL->tcpstream = NIL;
- return NIL;
- }
- if (stream->debug) mm_dlog (LOCAL->reply.line);
- LOCAL->reply.key = NIL; /* init fields in case error */
- LOCAL->reply.text = NIL;
- /* parse separate tag, key, text */
- if (!((LOCAL->reply.tag = (char *) strtok (LOCAL->reply.line," ")) &&
- (LOCAL->reply.key = (char *) strtok (NIL," ")))) {
- /* determine what is missing */
- if (!LOCAL->reply.tag) strcpy (LOCAL->tmp,"IMAP server sent a blank line");
- else sprintf (LOCAL->tmp,"Missing IMAP reply key: %.80s",LOCAL->reply.tag);
- mm_log (LOCAL->tmp,WARN); /* pass up the barfage */
- return NIL; /* can't parse this text */
- }
- ucase (LOCAL->reply.key); /* make sure key is upper case */
- /* get text as well, allow empty text */
- if (!(LOCAL->reply.text = (char *) strtok (NIL,"\n")))
- LOCAL->reply.text = LOCAL->reply.key + strlen (LOCAL->reply.key);
- return &LOCAL->reply; /* return parsed reply */
- }
-
- /* Mail Access Protocol fake reply
- * Accepts: MAIL stream
- * tag
- * text of fake reply
- * Returns: parsed reply
- */
-
- IMAPPARSEDREPLY *imap_fake (stream,tag,text)
- MAILSTREAM *stream;
- char *tag;
- char *text;
- {
- /* build fake reply string */
- sprintf (LOCAL->tmp,"%s %s",tag,text);
- /* parse and return it */
- return imap_parse_reply (stream,cpystr (LOCAL->tmp));
- }
-
-
- /* Mail Access Protocol check for OK response in tagged reply
- * Accepts: MAIL stream
- * parsed reply
- * Returns: T if OK else NIL
- */
-
- long imap_OK (stream,reply)
- MAILSTREAM *stream;
- IMAPPARSEDREPLY *reply;
- {
- /* OK - operation succeeded */
- if (!strcmp (reply->key,"OK")) return T;
- /* NO - operation failed */
- else if (strcmp (reply->key,"NO")) {
- /* BAD - operation rejected */
- if (!strcmp (reply->key,"BAD"))
- sprintf (LOCAL->tmp,"IMAP error: %.80s",reply->text);
- /* BYE - server is going away */
- else if (!strcmp (reply->key,"BYE")) strcpy (LOCAL->tmp,reply->text);
- else sprintf (LOCAL->tmp,"Unexpected IMAP response: %.80s %.80s",
- reply->key,reply->text);
- mm_log (LOCAL->tmp,WARN); /* log the sucker */
- }
- return NIL;
- }
-
- /* Mail Access Protocol parse and act upon unsolicited reply
- * Accepts: MAIL stream
- * parsed reply
- */
-
- void imap_parse_unsolicited (stream,reply)
- MAILSTREAM *stream;
- IMAPPARSEDREPLY *reply;
- {
- unsigned long i,j;
- long msgno;
- char *endptr;
- char *keyptr,*txtptr;
- /* see if key is a number */
- msgno = strtol (reply->key,&endptr,10);
- if (*endptr) { /* if non-numeric */
- if (!strcmp (reply->key,"FLAGS")) imap_parse_flaglst (stream,reply);
- else if (!strcmp (reply->key,"SEARCH")) imap_searched (stream,reply->text);
- else if (!strcmp (reply->key,"MAILBOX")) {
- sprintf (LOCAL->tmp,"{%s}%s",imap_host (stream),reply->text);
- mm_mailbox (((*reply->text != '{') ||
- (*(strchr (stream->mailbox,'}')+1) == '{')) ?
- LOCAL->tmp : reply->text);
- }
- else if (!strcmp (reply->key,"BBOARD")) {
- sprintf (LOCAL->tmp,"{%s}%s",imap_host (stream),reply->text);
- mm_bboard (((*reply->text != '{') ||
- (*(strchr (stream->mailbox,'}')+1) == '{')) ?
- LOCAL->tmp : reply->text);
- }
- else if (!strcmp (reply->key,"BYE")) {
- if (!stream->silent) mm_log (reply->text,(long) NIL);
- }
- else if (!strcmp (reply->key,"OK"))
- mm_notify (stream,reply->text,(long) NIL);
- else if (!strcmp (reply->key,"NO")) {
- if (!stream->silent) mm_notify (stream,reply->text,WARN);
- }
- else if (!strcmp (reply->key,"BAD")) mm_notify (stream,reply->text,ERROR);
- else {
- sprintf (LOCAL->tmp,"Unexpected unsolicited message: %.80s",reply->key);
- mm_log (LOCAL->tmp,WARN);
- }
- }
-
- else { /* if numeric, a keyword follows */
- /* deposit null at end of keyword */
- keyptr = ucase ((char *) strtok (reply->text," "));
- /* and locate the text after it */
- txtptr = (char *) strtok (NIL,"\n");
- /* now take the action */
- /* change in size of mailbox */
- if (!strcmp (keyptr,"EXISTS")) {
- if (msgno > LOCAL->cachesize) {
- i = (j = msgno + CACHEINCREMENT) * sizeof (char *);
- if (LOCAL->cachesize) { /* resize extant caches */
- fs_resize ((void **) &LOCAL->header,i);
- fs_resize ((void **) &LOCAL->text,i);
- }
- else { /* instantiate new caches */
- LOCAL->header = (char **) fs_get (i);
- LOCAL->text = (char **) fs_get (i);
- }
- /* init new space in caches */
- for (i = LOCAL->cachesize; i < j; i++)
- LOCAL->header[i] = LOCAL->text[i] = NIL;
- LOCAL->cachesize = j;
- }
- mail_exists (stream,msgno);
- }
- else if (!strcmp (keyptr,"RECENT")) mail_recent (stream,msgno);
- else if (!strcmp (keyptr,"EXPUNGE")) imap_expunged (stream,msgno);
- else if (!strcmp (keyptr,"FETCH"))
- imap_parse_data (stream,msgno,txtptr,reply);
- /* obsolete alias for FETCH */
- else if (!strcmp (keyptr,"STORE"))
- imap_parse_data (stream,msgno,txtptr,reply);
- /* obsolete response to COPY */
- else if (strcmp (keyptr,"COPY")) {
- sprintf (LOCAL->tmp,"Unknown message data: %ld %.80s",msgno,keyptr);
- mm_log (LOCAL->tmp,WARN);
- }
- }
- }
-
- /* Mail Access Protocol parse flag list
- * Accepts: MAIL stream
- * parsed reply
- *
- * The reply->line is yanked out of the parsed reply and stored on
- * stream->flagstring. This is the original fs_get'd reply string, and
- * has all the flagstrings embedded in it.
- */
-
- void imap_parse_flaglst (stream,reply)
- MAILSTREAM *stream;
- IMAPPARSEDREPLY *reply;
- {
- char *text = reply->text;
- char *flag;
- long i;
- /* flush old flagstring and flags if any */
- if (stream->flagstring) fs_give ((void **) &stream->flagstring);
- for (i = 0; i < NUSERFLAGS; ++i) stream->user_flags[i] = NIL;
- /* remember this new one */
- stream->flagstring = reply->line;
- reply->line = NIL;
- ++text; /* skip past open parenthesis */
- /* get first flag if any */
- if (flag = (char *) strtok (text," )")) {
- i = 0; /* init flag index */
- /* add all user flags */
- do if (*flag != '\\') stream->user_flags[i++] = flag;
- while (flag = (char *) strtok (NIL," )"));
- }
- }
-
-
- /* Mail Access Protocol messages have been searched out
- * Accepts: MAIL stream
- * list of message numbers
- *
- * Calls external "mail_searched" function to notify main program
- */
-
- void imap_searched (stream,text)
- MAILSTREAM *stream;
- char *text;
- {
- /* only do something if have text */
- if (text && (text = (char *) strtok (text," ")))
- for (; text; text = (char *) strtok (NIL," "))
- mail_searched (stream,atol (text));
- }
-
- /* Mail Access Protocol message has been expunged
- * Accepts: MAIL stream
- * message number
- *
- * Calls external "mail_searched" function to notify main program
- */
-
- void imap_expunged (stream,msgno)
- MAILSTREAM *stream;
- long msgno;
- {
- long i = msgno - 1;
- if (LOCAL->header[i]) fs_give ((void **) &LOCAL->header[i]);
- if (LOCAL->text[i]) fs_give ((void **) &LOCAL->text[i]);
- /* slide down remainder of cache */
- for (i = msgno; i <= stream->nmsgs; ++i) {
- LOCAL->header[i - 1] = LOCAL->header[i];
- LOCAL->text[i - 1] = LOCAL->text[i];
- }
- LOCAL->header[i] = LOCAL->text[i] = NIL;
- mail_expunged (stream,msgno); /* notify upper level */
- }
-
-
- /* Mail Access Protocol parse data
- * Accepts: MAIL stream
- * message #
- * text to parse
- * parsed reply
- *
- * This code should probably be made a bit more paranoid about malformed
- * S-expressions.
- */
-
- void imap_parse_data (stream,msgno,text,reply)
- MAILSTREAM *stream;
- long msgno;
- char *text;
- IMAPPARSEDREPLY *reply;
- {
- char *prop;
- MESSAGECACHE *elt = mail_elt (stream,msgno);
- ++text; /* skip past open parenthesis */
- /* parse Lisp-form property list */
- while (prop = (char *) strtok (text," )")) {
- /* point at value */
- text = (char *) strtok (NIL,"\n");
- /* parse the property and its value */
- imap_parse_prop (stream,elt,ucase (prop),&text,reply);
- }
- }
-
- /* Mail Access Protocol parse property
- * Accepts: MAIL stream
- * cache item
- * property name
- * property value text pointer
- * parsed reply
- */
-
- void imap_parse_prop (stream,elt,prop,txtptr,reply)
- MAILSTREAM *stream;
- MESSAGECACHE *elt;
- char *prop;
- char **txtptr;
- IMAPPARSEDREPLY *reply;
- {
- char *s;
- long i = elt->msgno - 1;
- if (!strcmp (prop,"ENVELOPE"))
- imap_parse_envelope (stream,&elt->env,txtptr,reply);
- else if (!strcmp (prop,"FLAGS")) imap_parse_flags (stream,elt,txtptr);
- else if (!strcmp (prop,"INTERNALDATE")) {
- if (elt->internal_date) fs_give ((void **) &elt->internal_date);
- elt->internal_date = imap_parse_string (stream,txtptr,reply,(long) NIL);
- }
- else if (!strcmp (prop,"RFC822.HEADER")) {
- if (LOCAL->header[i]) fs_give ((void **) &LOCAL->header[i]);
- LOCAL->header[i] = imap_parse_string (stream,txtptr,reply,elt->msgno);
- }
- else if (!strcmp (prop,"RFC822.SIZE"))
- elt->rfc822_size = imap_parse_number (stream,txtptr);
- else if (!strcmp (prop,"RFC822.TEXT")) {
- if (LOCAL->text[i]) fs_give ((void **) &LOCAL->text[i]);
- LOCAL->text[i] = imap_parse_string (stream,txtptr,reply,elt->msgno);
- }
- else if (prop[0] == 'B' && prop[1] == 'O' && prop[2] == 'D' &&
- prop[3] == 'Y') {
- s = cpystr (prop+4); /* copy segment specifier */
- imap_parse_body (stream,elt->msgno,&elt->body,s,txtptr,reply);
- fs_give ((void **) &s);
- }
- /* this shouldn't happen with our client */
- else if (!strcmp (prop,"RFC822")) {
- if (LOCAL->text[i]) fs_give ((void **) &LOCAL->text[i]);
- LOCAL->text[i] = imap_parse_string (stream,txtptr,reply,elt->msgno);
- }
- else {
- sprintf (LOCAL->tmp,"Unknown message property: %.80s",prop);
- mm_log (LOCAL->tmp,WARN);
- }
- }
-
- /* Mail Access Protocol parse envelope
- * Accepts: MAIL stream
- * pointer to envelope pointer
- * current text pointer
- * parsed reply
- *
- * Updates text pointer
- */
-
- void imap_parse_envelope (stream,env,txtptr,reply)
- MAILSTREAM *stream;
- ENVELOPE **env;
- char **txtptr;
- IMAPPARSEDREPLY *reply;
- {
- char c = *((*txtptr)++); /* grab first character */
- /* ignore leading spaces */
- while (c == ' ') c = *((*txtptr)++);
- /* free any old envelope */
- if (*env) mail_free_envelope (env);
- switch (c) { /* dispatch on first character */
- case '(': /* if envelope S-expression */
- *env = mail_newenvelope (); /* parse the new envelope */
- (*env)->date = imap_parse_string (stream,txtptr,reply,(long) NIL);
- (*env)->subject = imap_parse_string (stream,txtptr,reply,(long) NIL);
- (*env)->from = imap_parse_adrlist (stream,txtptr,reply);
- (*env)->sender = imap_parse_adrlist (stream,txtptr,reply);
- (*env)->reply_to = imap_parse_adrlist (stream,txtptr,reply);
- (*env)->to = imap_parse_adrlist (stream,txtptr,reply);
- (*env)->cc = imap_parse_adrlist (stream,txtptr,reply);
- (*env)->bcc = imap_parse_adrlist (stream,txtptr,reply);
- (*env)->in_reply_to = imap_parse_string (stream,txtptr,reply,(long) NIL);
- (*env)->message_id = imap_parse_string (stream,txtptr,reply,(long) NIL);
- if (**txtptr != ')') {
- sprintf (LOCAL->tmp,"Junk at end of envelope: %.80s",*txtptr);
- mm_log (LOCAL->tmp,WARN);
- }
- else ++*txtptr; /* skip past delimiter */
- break;
- case 'N': /* if NIL */
- case 'n':
- ++*txtptr; /* bump past "I" */
- ++*txtptr; /* bump past "L" */
- break;
- default:
- sprintf (LOCAL->tmp,"Not an envelope: %.80s",*txtptr);
- mm_log (LOCAL->tmp,WARN);
- break;
- }
- }
-
- /* Mail Access Protocol parse address list
- * Accepts: MAIL stream
- * current text pointer
- * parsed reply
- * Returns: address list, NIL on failure
- *
- * Updates text pointer
- */
-
- ADDRESS *imap_parse_adrlist (stream,txtptr,reply)
- MAILSTREAM *stream;
- char **txtptr;
- IMAPPARSEDREPLY *reply;
- {
- ADDRESS *adr = NIL;
- char c = **txtptr; /* sniff at first character */
- /* ignore leading spaces */
- while (c == ' ') c = *++*txtptr;
- ++*txtptr; /* skip past open paren */
- switch (c) {
- case '(': /* if envelope S-expression */
- adr = imap_parse_address (stream,txtptr,reply);
- if (**txtptr != ')') {
- sprintf (LOCAL->tmp,"Junk at end of address list: %.80s",*txtptr);
- mm_log (LOCAL->tmp,WARN);
- }
- else ++*txtptr; /* skip past delimiter */
- break;
- case 'N': /* if NIL */
- case 'n':
- ++*txtptr; /* bump past "I" */
- ++*txtptr; /* bump past "L" */
- break;
- default:
- sprintf (LOCAL->tmp,"Not an address: %.80s",*txtptr);
- mm_log (LOCAL->tmp,WARN);
- break;
- }
- return adr;
- }
-
- /* Mail Access Protocol parse address
- * Accepts: MAIL stream
- * current text pointer
- * parsed reply
- * Returns: address, NIL on failure
- *
- * Updates text pointer
- */
-
- ADDRESS *imap_parse_address (stream,txtptr,reply)
- MAILSTREAM *stream;
- char **txtptr;
- IMAPPARSEDREPLY *reply;
- {
- ADDRESS *adr = NIL;
- ADDRESS *ret = NIL;
- ADDRESS *prev = NIL;
- char c = **txtptr; /* sniff at first address character */
- switch (c) {
- case '(': /* if envelope S-expression */
- while (c == '(') { /* recursion dies on small stack machines */
- ++*txtptr; /* skip past open paren */
- if (adr) prev = adr; /* note previous if any */
- adr = mail_newaddr (); /* instantiate address and parse its fields */
- adr->personal = imap_parse_string (stream,txtptr,reply,(long) NIL);
- adr->adl = imap_parse_string (stream,txtptr,reply,(long) NIL);
- adr->mailbox = imap_parse_string (stream,txtptr,reply,(long) NIL);
- adr->host = imap_parse_string (stream,txtptr,reply,(long) NIL);
- if (**txtptr != ')') { /* handle trailing paren */
- sprintf (LOCAL->tmp,"Junk at end of address: %.80s",*txtptr);
- mm_log (LOCAL->tmp,WARN);
- }
- else ++*txtptr; /* skip past close paren */
- c = **txtptr; /* set up for while test */
- /* ignore leading spaces in front of next */
- while (c == ' ') c = *++*txtptr;
- if (adr->mailbox) { /* make sure address has a mailbox */
- /* if no host default to server */
- if (!adr->host) adr->host = cpystr (imap_host (stream));
- if (!ret) ret = adr; /* if first time note first adr */
- /* if previous link new block to it */
- if (prev) prev->next = adr;
- }
- else { /* server return sick address, flush it */
- mail_free_address (&adr);
- adr = NIL; /* get a new one next time through the loop */
- }
- }
- break;
-
- case 'N': /* if NIL */
- case 'n':
- *txtptr += 3; /* bump past NIL */
- break;
- default:
- sprintf (LOCAL->tmp,"Not an address: %.80s",*txtptr);
- mm_log (LOCAL->tmp,WARN);
- break;
- }
- return ret;
- }
-
- /* Mail Access Protocol parse flags
- * Accepts: current message cache
- * current text pointer
- *
- * Updates text pointer
- */
-
- void imap_parse_flags (stream,elt,txtptr)
- MAILSTREAM *stream;
- MESSAGECACHE *elt;
- char **txtptr;
- {
- char *flag;
- char c;
- elt->user_flags = NIL; /* zap old flag values */
- elt->seen = elt->deleted = elt->flagged = elt->answered = elt->recent = NIL;
- while (T) { /* parse list of flags */
- flag = ++*txtptr; /* point at a flag */
- /* scan for end of flag */
- while (**txtptr != ' ' && **txtptr != ')') ++*txtptr;
- c = **txtptr; /* save delimiter */
- **txtptr = '\0'; /* tie off flag */
- if (*flag != '\0') { /* if flag is non-null */
- if (*flag == '\\') { /* if starts with \ must be sys flag */
- if (!strcmp (ucase (flag),"\\SEEN")) elt->seen = T;
- else if (!strcmp (flag,"\\DELETED")) elt->deleted = T;
- else if (!strcmp (flag,"\\FLAGGED")) elt->flagged = T;
- else if (!strcmp (flag,"\\ANSWERED")) elt->answered = T;
- else if (!strcmp (flag,"\\RECENT")) elt->recent = T;
- /* coddle TOPS-20 server */
- else if (strcmp (flag,"\\XXXX") && strcmp (flag,"\\YYYY") &&
- strncmp (flag,"\\UNDEFINEDFLAG",14)) {
- sprintf (LOCAL->tmp,"Unknown system flag: %.80s",flag);
- mm_log (LOCAL->tmp,WARN);
- }
- }
- /* otherwise user flag */
- else imap_parse_user_flag (stream,elt,flag);
- }
- if (c == ')') break; /* quit if end of list */
- }
- ++*txtptr; /* bump past delimiter */
- }
-
- /* Mail Access Protocol parse user flag
- * Accepts: message cache element
- * flag name
- */
-
- void imap_parse_user_flag (stream,elt,flag)
- MAILSTREAM *stream;
- MESSAGECACHE *elt;
- char *flag;
- {
- long i;
- /* sniff through all user flags */
- for (i = 0; i < NUSERFLAGS; ++i)
- /* match this one? */
- if (!strcmp (flag,stream->user_flags[i])) {
- elt->user_flags |= 1 << i;/* yes, set the bit for that flag */
- return; /* and quit */
- }
- sprintf (LOCAL->tmp,"Unknown user flag: %.80s",flag);
- mm_log (LOCAL->tmp,WARN);
- }
-
- /* Mail Access Protocol parse string
- * Accepts: MAIL stream
- * current text pointer
- * parsed reply
- * flag that it may be kept outside of free storage cache
- * Returns: string
- *
- * Updates text pointer
- */
-
- char *imap_parse_string (stream,txtptr,reply,special)
- MAILSTREAM *stream;
- char **txtptr;
- IMAPPARSEDREPLY *reply;
- long special;
- {
- char *st;
- char *string = NIL;
- unsigned long i,j;
- char c = **txtptr; /* sniff at first character */
- /* ignore leading spaces */
- while (c == ' ') c = *++*txtptr;
- st = ++*txtptr; /* remember start of string */
- switch (c) {
- case '"': /* if quoted string */
- i = 1; /* initial byte count */
- while (**txtptr != '"') { /* search for end of string */
- ++i; /* bump count */
- ++*txtptr; /* bump pointer */
- }
- **txtptr = '\0'; /* tie off string */
- string = (char *) fs_get (i);
- strncpy (string,st,i); /* copy the string */
- ++*txtptr; /* bump past delimiter */
- break;
- case 'N': /* if NIL */
- case 'n':
- ++*txtptr; /* bump past "I" */
- ++*txtptr; /* bump past "L" */
- break;
-
- case '{': /* if literal string */
- /* get size of string */
- i = imap_parse_number (stream,txtptr);
- if (special && mailgets) /* have special routine to slurp string? */
- string = (*mailgets) (tcp_getbuffer,LOCAL->tcpstream,i);
- else { /* must slurp into free storage */
- string = (char *) fs_get (i + 1);
- *string = '\0'; /* init in case getbuffer fails */
- /* get the literal */
- tcp_getbuffer (LOCAL->tcpstream,i,string);
- }
- fs_give ((void **) &reply->line);
- /* get new reply text line */
- reply->line = tcp_getline (LOCAL->tcpstream);
- if (stream->debug) mm_dlog (reply->line);
- *txtptr = reply->line; /* set text pointer to point at it */
- break;
- default:
- sprintf (LOCAL->tmp,"Not a string: %c%.80s",c,*txtptr);
- mm_log (LOCAL->tmp,WARN);
- break;
- }
- return string;
- }
-
-
- /* Mail Access Protocol parse number
- * Accepts: MAIL stream
- * current text pointer
- * Returns: number parsed
- *
- * Updates text pointer
- */
-
- unsigned long imap_parse_number (stream,txtptr)
- MAILSTREAM *stream;
- char **txtptr;
- { /* parse number */
- long i = strtol (*txtptr,txtptr,10);
- if (i < 0) { /* number valid? */
- sprintf (LOCAL->tmp,"Bad number: %ld",i);
- mm_log (LOCAL->tmp,WARN);
- i = 0; /* make sure valid */
- }
- return (unsigned long) i;
- }
-
- /* Mail Access Protocol parse body structure or contents
- * Accepts: MAIL stream
- * pointer to body pointer
- * pointer to segment
- * current text pointer
- * parsed reply
- *
- * Updates text pointer, stores body
- */
-
- void imap_parse_body (stream,msgno,body,seg,txtptr,reply)
- MAILSTREAM *stream;
- long msgno;
- BODY **body;
- char *seg;
- char **txtptr;
- IMAPPARSEDREPLY *reply;
- {
- char *s;
- unsigned long i;
- BODY *b;
- PART *part;
- switch (*seg++) { /* dispatch based on type of data */
- case '\0': /* body structure */
- mail_free_body (body); /* flush any prior body */
- /* instantiate and parse a new body */
- imap_parse_body_structure (stream,*body = mail_newbody (),txtptr,reply);
- break;
- case '[': /* body section text */
- if ((!(s = strchr (seg,']'))) || s[1]) {
- sprintf (LOCAL->tmp,"Bad body index: %.80s",seg);
- mm_log (LOCAL->tmp,WARN);
- return;
- }
- *s = '\0'; /* tie off section specifier */
- /* get the body section text */
- s = imap_parse_string (stream,txtptr,reply,msgno);
- if (!(b = *body)) { /* must have structure first */
- mm_log ("Body contents received when body structure unknown",WARN);
- fs_give ((void **) &s);
- return;
- }
- /* get first section number */
- if (!(seg && *seg && ((i = strtol (seg,&seg,10)) > 0))) {
- mm_log ("Bogus section number",WARN);
- fs_give ((void **) &s);
- return;
- }
-
- do { /* multipart content? */
- if (b->type == TYPEMULTIPART) {
- part = b->contents.part;/* yes, find desired part */
- while (--i && (part = part->next));
- if (!part || (((b = &part->body)->type == TYPEMULTIPART) && !*s)) {
- mm_log ("Bad section number",WARN);
- fs_give ((void **) &s);
- return;
- }
- }
- else if (i != 1) { /* otherwise must be section 1 */
- mm_log ("Invalid section number",WARN);
- fs_give ((void **) &s);
- return;
- }
- /* need to go down further? */
- if (i = *seg) switch (b->type) {
- case TYPEMESSAGE: /* embedded message, get body */
- b = b->contents.msg.body;
- case TYPEMULTIPART: /* multipart, get next section */
- if ((*seg++ == '.') && (i = strtol (seg,&seg,10)) > 0) break;
- default: /* bogus subpart */
- mm_log ("Invalid sub-section",WARN);
- fs_give ((void **) &s);
- return;
- }
- } while (i);
- if (b) switch (b->type) { /* decide where the data goes based on type */
- case TYPEMULTIPART: /* nothing to fetch with these */
- mm_log ("Textual body contents received for MULTIPART body part",WARN);
- fs_give ((void **) &s);
- return;
- case TYPEMESSAGE: /* encapsulated message */
- fs_give ((void **) &b->contents.msg.text);
- b->contents.msg.text = s;
- break;
- case TYPETEXT: /* textual data */
- fs_give ((void **) &b->contents.text);
- b->contents.text = (unsigned char *) s;
- break;
- default: /* otherwise assume it is binary */
- fs_give ((void **) &b->contents.binary);
- b->contents.binary = (void *) s;
- break;
- }
- break;
- default: /* bogon */
- sprintf (LOCAL->tmp,"Bad body fetch: %.80s",seg);
- mm_log (LOCAL->tmp,WARN);
- return;
- }
- }
-
- /* Mail Access Protocol parse body structure
- * Accepts: MAIL stream
- * current text pointer
- * parsed reply
- * body structure to write into
- *
- * Updates text pointer
- */
-
- void imap_parse_body_structure (stream,body,txtptr,reply)
- MAILSTREAM *stream;
- BODY *body;
- char **txtptr;
- IMAPPARSEDREPLY *reply;
- {
- char *s;
- PART *part = NIL;
- PARAMETER *param = NIL;
- char c = *((*txtptr)++); /* grab first character */
- /* ignore leading spaces */
- while (c == ' ') c = *((*txtptr)++);
- switch (c) { /* dispatch on first character */
- case '(': /* body structure list */
- if (**txtptr == '(') { /* multipart body? */
- body->type= TYPEMULTIPART;/* yes, set its type */
- do { /* instantiate new body part */
- if (part) part = part->next = mail_newbody_part ();
- else body->contents.part = part = mail_newbody_part ();
- /* parse it */
- imap_parse_body_structure (stream,&part->body,txtptr,reply);
- } while (**txtptr == '(');/* for each body part */
- if (!(body->subtype = imap_parse_string (stream,txtptr,reply,(long)NIL)))
- mm_log ("Missing multipart subtype",WARN);
- if (**txtptr != ')') { /* validate ending */
- sprintf (LOCAL->tmp,"Junk at end of multipart body: %.80s",*txtptr);
- mm_log (LOCAL->tmp,WARN);
- }
- else ++*txtptr; /* skip past delimiter */
- }
-
- else { /* not multipart, parse type name */
- if (**txtptr == ')') { /* empty body? */
- ++*txtptr; /* bump past it */
- break; /* and punt */
- }
- body->type = TYPEOTHER; /* assume unknown type */
- body->encoding = ENCOTHER;/* and unknown encoding */
- /* parse type */
- if (s = imap_parse_string (stream,txtptr,reply,(long) NIL)) {
- ucase (s); /* make parse easier */
- switch (*s++) { /* dispatch based on type */
- case 'A': /* APPLICATION or AUDIO */
- if (!strcmp (s,"PPLICATION")) body->type = TYPEAPPLICATION;
- else if (!strcmp (s,"UDIO")) body->type = TYPEAUDIO;
- break;
- case 'I': /* IMAGE */
- if (!strcmp (s,"MAGE")) body->type = TYPEIMAGE;
- break;
- case 'M': /* MESSAGE */
- if (!strcmp (s,"ESSAGE")) body->type = TYPEMESSAGE;
- break;
- case 'T': /* TEXT */
- if (!strcmp (s,"EXT")) body->type = TYPETEXT;
- break;
- case 'V': /* VIDEO */
- if (!strcmp (s,"IDEO")) body->type = TYPEVIDEO;
- break;
- default:
- break;
- }
- }
- /* parse subtype */
- body->subtype = imap_parse_string (stream,txtptr,reply,(long) NIL);
-
- body->parameter = NIL; /* init parameter list */
-
- c = *(*txtptr)++; /* sniff at first character */
- /* ignore leading spaces */
- while (c == ' ') c = *(*txtptr)++;
- /* parse parameter list */
- if (c == '(') while (c != ')') {
- if (body->parameter) /* append new parameter to tail */
- param = param->next = mail_newbody_parameter ();
- else body->parameter = param = mail_newbody_parameter ();
- if (!(param->attribute = imap_parse_string (stream,txtptr,reply,
- (long) NIL))){
- mm_log ("Missing parameter attribute",WARN);
- break;
- }
- if (!(param->value = imap_parse_string (stream,txtptr,reply,
- (long) NIL))) {
- sprintf (LOCAL->tmp,"Missing value for parameter %.80s",
- param->attribute);
- mm_log (LOCAL->tmp,WARN);
- break;
- }
- switch (c = **txtptr) { /* see what comes after */
- case ' ': /* flush whitespace */
- while ((c = *++*txtptr) == ' ');
- break;
- case ')': /* end of attribute/value pairs */
- ++*txtptr; /* skip past closing paren */
- break;
- default:
- sprintf (LOCAL->tmp,"Junk at end of parameter: %.80s",s);
- mm_log (LOCAL->tmp,WARN);
- break;
- }
- }
- else { /* empty parameter, must be NIL */
- if (((c == 'N') || (c == 'n')) &&
- ((*(s = *txtptr) == 'I') || (*s == 'i')) &&
- ((s[1] == 'L') || (s[1] == 'l')) && (s[2] == ' ')) *txtptr += 2;
- else {
- sprintf (LOCAL->tmp,"Bogus body parameter: %c%.80s",c,s);
- mm_log (LOCAL->tmp,WARN);
- break;
- }
- }
-
- body->id = imap_parse_string (stream,txtptr,reply,(long) NIL);
- body->description = imap_parse_string (stream,txtptr,reply,(long) NIL);
- if (s = imap_parse_string (stream,txtptr,reply,(long) NIL)) {
- ucase (s); /* make parse easier */
- switch (*s++) { /* dispatch based on encoding */
- case '7': /* 7BIT */
- if (!strcmp (s,"BIT")) body->encoding = ENC7BIT;
- break;
- case '8': /* 8BIT */
- if (!strcmp (s,"BIT")) body->encoding = ENC8BIT;
- break;
- case 'B': /* BASE64 or BINARY */
- if (!strcmp (s,"ASE64")) body->encoding = ENCBASE64;
- else if (!strcmp (s,"INARY")) body->encoding = ENCBINARY;
- break;
- case 'Q': /* QUOTED-PRINTABLE */
- if (!strcmp (s,"UOTED-PRINTABLE"))
- body->encoding = ENCQUOTEDPRINTABLE;
- break;
- default:
- break;
- }
- }
- /* parse size of contents in bytes */
- body->size.bytes = imap_parse_number (stream,txtptr);
- switch (body->type) { /* possible extra stuff */
- case TYPEMESSAGE: /* message envelope and body */
- if (strcmp (body->subtype,"RFC822")) break;
- imap_parse_envelope (stream,&body->contents.msg.env,txtptr,reply);
- body->contents.msg.body = mail_newbody ();
- imap_parse_body_structure(stream,body->contents.msg.body,txtptr,reply);
- /* drop into text case */
- case TYPETEXT: /* size in lines */
- body->size.lines = imap_parse_number (stream,txtptr);
- break;
- default: /* otherwise nothing special */
- break;
- }
- if (**txtptr != ')') { /* validate ending */
- sprintf (LOCAL->tmp,"Junk at end of body part: %.80s",*txtptr);
- mm_log (LOCAL->tmp,WARN);
- }
- else ++*txtptr; /* skip past delimiter */
- }
- break;
-
- case 'N': /* if NIL */
- case 'n':
- ++*txtptr; /* bump past "I" */
- ++*txtptr; /* bump past "L" */
- break;
- default: /* otherwise quite bogus */
- sprintf (LOCAL->tmp,"Bogus body structure: %.80s",*txtptr);
- mm_log (LOCAL->tmp,WARN);
- break;
- }
- }
-